﻿using System.Diagnostics;
using System.Text;
using RDF;

using stepengine = RDF.ifcengine;

#if _WIN64
using int_t = System.Int64;
#else
using int_t = System.Int32;
#endif

namespace DZJIFC.Model
{
    public class AP242Model : Model
    {
        #region Methods

        public AP242Model()
            : base(ModelType.AP242)
        {
        }

        protected override bool LoadCore(string strFilePath)
        {
            if (!File.Exists(strFilePath))
            {
                return false;
            }

            Instance = stepengine.sdaiOpenModelBNUnicode(0, Encoding.Unicode.GetBytes(strFilePath), Encoding.Unicode.GetBytes(""));
            if (Instance == 0)
            {
                return false;
            }

            LoadProductDefinitions();

            LoadAssemblies();

            LoadGeometry();

            Scale();

            FireModelLoaded();

            return true;
        }

        private void LoadProductDefinitions()
        {
            int_t pAggr = stepengine.sdaiGetEntityExtentBN(Instance, "PRODUCT_DEFINITION");
            int_t iMembersCount = stepengine.sdaiGetMemberCount(pAggr);
            for (int_t i = 0; i < iMembersCount; ++i)
            {
                int_t iInstance = 0;
                stepengine.engiGetAggrElement(pAggr, i, stepengine.sdaiINSTANCE, out iInstance);
                Debug.Assert(iInstance != 0);

                PreLoadInstance(iInstance);

                var product = new AP242Product(iInstance);
                Debug.Assert(!ExpressID2Product.ContainsKey(product.ExpressID));
                ExpressID2Product[product.ExpressID] = product;

                Debug.Assert(!Geometries.ContainsKey(product.Instance));
                Geometries[product.Instance] = product;
            }
        }

        private void LoadAssemblies()
        {
            int_t pAggr = stepengine.sdaiGetEntityExtentBN(Instance, "NEXT_ASSEMBLY_USAGE_OCCURRENCE");
            int_t iMembersCount = stepengine.sdaiGetMemberCount(pAggr);
            for (int_t i = 0; i < iMembersCount; ++i)
            {
                int_t iInstance = 0;
                stepengine.sdaiGetAggrByIndex(pAggr, i, stepengine.sdaiINSTANCE, out iInstance);
                Debug.Assert(iInstance != 0);

                int_t iRelatingProductInstance;
                stepengine.sdaiGetAttrBN(iInstance, "relating_product_definition", stepengine.sdaiINSTANCE, out iRelatingProductInstance);
                Debug.Assert(iRelatingProductInstance != 0);

                var relatingProduct = GetProduct(iRelatingProductInstance, true, false);

                int_t iRelatedProductInstance;
                stepengine.sdaiGetAttrBN(iInstance, "related_product_definition", stepengine.sdaiINSTANCE, out iRelatedProductInstance);
                Debug.Assert(iRelatedProductInstance != 0);

                var relatedProduct = GetProduct(iRelatedProductInstance, false, true);

                var assembly = new AP242Assembly(iInstance, relatingProduct, relatedProduct);
                Debug.Assert(!ExpressID2Assembly.ContainsKey(assembly.ExpressID));
                ExpressID2Assembly[assembly.ExpressID] = assembly;
            }
        }

        private void LoadGeometry()
        {
            foreach (var product in ExpressID2Product.Values)
            {
                if (product.RelatedProductsCount == 0)
                {
                    WalkAssemblyTreeRecursively(product, null);
                }
            }
        }

        private void WalkAssemblyTreeRecursively(AP242Product product, _matrix4x3 parentMatrix)
        {
            foreach (var assembly in ExpressID2Assembly.Values)
            {
                if (assembly.RelatingProduct != product)
                {
                    continue;
                }

                long iOwlTransformationInstance = 0;
                stepengine.owlBuildInstance(Instance, assembly.Instance, out iOwlTransformationInstance);
                Debug.Assert(iOwlTransformationInstance != 0);

                _matrix4x3 matrix = new _matrix4x3();

                var iOwlMatrixInstance = GetObjectProperty(iOwlTransformationInstance, "matrix");
                if (iOwlMatrixInstance != 0)
                {
                    RDF.engine.InferenceInstance(iOwlMatrixInstance);
                    matrix._11 = GetDoubleProperty(iOwlMatrixInstance, "_11");
                    matrix._12 = GetDoubleProperty(iOwlMatrixInstance, "_12");
                    matrix._13 = GetDoubleProperty(iOwlMatrixInstance, "_13");
                    matrix._21 = GetDoubleProperty(iOwlMatrixInstance, "_21");
                    matrix._22 = GetDoubleProperty(iOwlMatrixInstance, "_22");
                    matrix._23 = GetDoubleProperty(iOwlMatrixInstance, "_23");
                    matrix._31 = GetDoubleProperty(iOwlMatrixInstance, "_31");
                    matrix._32 = GetDoubleProperty(iOwlMatrixInstance, "_32");
                    matrix._33 = GetDoubleProperty(iOwlMatrixInstance, "_33");
                    matrix._41 = GetDoubleProperty(iOwlMatrixInstance, "_41");
                    matrix._42 = GetDoubleProperty(iOwlMatrixInstance, "_42");
                    matrix._43 = GetDoubleProperty(iOwlMatrixInstance, "_43");
                }

                if (parentMatrix != null)
                {
                    _matrix4x3.Multiply(matrix, parentMatrix, matrix);
                }

                WalkAssemblyTreeRecursively(assembly.RelatedProduct, matrix);
            }// foreach (var assembly in ...

            var instance = new AP242Instance(_iInstanceID++, product, parentMatrix);
            Debug.Assert(!ID2Instance.ContainsKey(instance.ID));
            ID2Instance[instance.ID] = instance;

            product.Instances.Add(instance); // Geometry

            Debug.Assert(!Instances.ContainsKey(instance.ID));
            Instances[instance.ID] = instance;
        }

        private AP242Product GetProduct(int_t iProductDefinitionInstance, bool bRelatingProduct, bool bRelatedProduct)
        {
            long iExpressID = stepengine.internalGetP21Line(iProductDefinitionInstance);
            if (!ExpressID2Product.TryGetValue(iExpressID, out var product))
            {
                PreLoadInstance(iProductDefinitionInstance);

                product = new AP242Product(iProductDefinitionInstance);
                Debug.Assert(!ExpressID2Product.ContainsKey(product.ExpressID));
                ExpressID2Product[product.ExpressID] = product;

                Debug.Assert(!Geometries.ContainsKey(product.Instance));
                Geometries[product.Instance] = product;
            }

            if (bRelatingProduct)
            {
                product.RelatingProductsCount++;
            }
            else if (bRelatedProduct)
            {
                product.RelatedProductsCount++;
            }
            else
            {
                Debug.Assert(false);
            }

            return product;
        }

        protected override void Reset()
        {
            base.Reset();

            ExpressID2Product = new Dictionary<long, AP242Product>();
            ExpressID2Assembly = new Dictionary<long, AP242Assembly>();
            ID2Instance = new Dictionary<int_t, AP242Instance>();
        }

        #endregion // Methods

        #region Properties

        public Dictionary<long, AP242Product> ExpressID2Product { get; private set; } = new Dictionary<long, AP242Product>(); // #todo - use Geometries?
        public Dictionary<long, AP242Assembly> ExpressID2Assembly { get; private set; } = new Dictionary<long, AP242Assembly>();
        public Dictionary<int_t, AP242Instance> ID2Instance { get; private set; } = new Dictionary<int_t, AP242Instance>(); // #todo - use Instances?

        #endregion // Properties
    }
}
